package cn.virens.docs;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.virens.common.StringUtil;
import cn.virens.web.annotations.PostmanDoc;

public class PostmanAPIDoc {
	private static final String schema = "https://schema.getpostman.com/json/collection/v2.1.0/collection.json";
	private static final String packageName = "com.ccqlk.web.controller.api";
	private static final String projectName = "智慧路灯";

	private static final String path = "C:/Users/ccqlk-loioi/Desktop/postman.json";

	public static void main(String[] args) {
		List<JSONObject> itemList = new ArrayList<>();

		for (Class<?> clazz : ClassUtil.scanPackageByAnnotation(packageName, Controller.class)) {
			RequestMapping mapping0 = clazz.getAnnotation(RequestMapping.class);
			PostmanDoc postman0 = clazz.getAnnotation(PostmanDoc.class);

			for (Method method : ReflectUtil.getMethods(clazz)) {
				PostmanDoc postman1 = method.getAnnotation(PostmanDoc.class);
				if (postman1 == null) continue;

				RequestMapping mapping1 = method.getAnnotation(RequestMapping.class);
				if (mapping1 == null) continue;

				if (mapping0 != null && mapping1 != null) {
					for (String p0 : path(mapping0)) {
						for (String p1 : path(mapping1)) {
							postman(itemList, postman0, postman1, mapping1, p0, p1);
						}
					}
				} else if (mapping1 != null) {
					for (String p1 : path(mapping1)) {
						postman(itemList, postman0, postman1, mapping1, p1);
					}
				}
			}
		}

		JSONObject info = new JSONObject();
		info.put("_postman_id", IdUtil.fastUUID());
		info.put("name", projectName);
		info.put("schema", schema);

		JSONArray eventList = new JSONArray();
		eventList.add(eventTest());

		JSONObject out = new JSONObject();
		out.put("event", eventList);
		out.put("item", itemList);
		out.put("info", info);

		// 输Postman导入文件
		FileUtil.writeString(JSON.toJSONString(out), path, "UTF-8");
	}

	private static JSONObject eventTest() {
		JSONArray exec = new JSONArray();
		exec.add("var jsonData = pm.response.json();");
		exec.add("");
		exec.add("//如果当前响应内容中存在sessionID就更新环境中的sessionID");
		exec.add("if(jsonData.token){");
		exec.add("    pm.environment.set(\"authorization\", jsonData.token);");
		exec.add("}");
		exec.add("");
		exec.add("//检查请求是否成功");
		exec.add("pm.test('请求成功:' + ( jsonData.message || '---'), function(){");
		exec.add("    return jsonData.success;");
		exec.add("});");

		JSONObject script = new JSONObject();
		script.put("type", "text/javascript");
		script.put("id", IdUtil.fastUUID());
		script.put("exec", exec);

		JSONObject test = new JSONObject();
		test.put("listen", "test");
		test.put("script", script);

		return test;
	}

	private static String path(String... paths) {
		StringBuilder builder = new StringBuilder("/");

		for (String path : paths) {
			if (path.startsWith("/")) {
				path = path.substring(1);
			}

			if (builder.charAt(builder.length() - 1) != '/') {
				builder.append("/").append(path);
			} else {
				builder.append(path);
			}
		}

		return builder.toString();

	}

	private static String[] path(RequestMapping mapping) {
		if (mapping == null) return new String[] {};

		if (mapping.path().length == 0) {
			return mapping.value();
		} else {
			return mapping.path();
		}
	}

	private static String path(PostmanDoc pm, String... paths) {
		return String.format("%s://%s:%s%s", pm.protocol(), pm.host(), pm.port(), path(paths));
	}

	private static JSONObject url(PostmanDoc pm, RequestMapping mp, String... path) {
		JSONObject url = new JSONObject();
		url.put("path", StrUtil.split(path(path), '/'));
		url.put("host", Arrays.asList(pm.host()));
		url.put("protocol", pm.protocol());
		url.put("raw", path(pm, path));
		url.put("port", pm.port());

		return url;
	}

	private static List<JSONObject> header(PostmanDoc pm, RequestMapping mp, String... path) {
		JSONObject author = new JSONObject();
		author.put("value", "{{authorization}}");
		author.put("key", "X-Auth-Token");
		author.put("type", "text");

		return Arrays.asList(author);
	}

	private static String method(PostmanDoc pm, RequestMapping mp, String... path) {
		if (mp != null && mp.method().length > 0) {
			return mp.method()[0].name();
		} else {
			return "GET";
		}
	}

	private static void postman(List<JSONObject> root, PostmanDoc pm0, PostmanDoc pm1, RequestMapping mp, String... path) {
		// 请求信息
		JSONObject request = new JSONObject();
		request.put("header", header(pm1, mp, path));
		request.put("method", method(pm1, mp, path));
		request.put("url", url(pm1, mp, path));

		// 接口信息
		JSONObject item = new JSONObject();
		item.put("name", name(pm0, pm1));
		item.put("request", request);

		// 区分是顶层还是分组
		if (StringUtil.isNotEmpty(group(pm1, pm0))) {
			for (JSONObject jsonObject : root) {
				if (StrUtil.equals(group(pm1, pm0), jsonObject.getString("name"))) {
					if (jsonObject.containsKey("request")) break;

					JSONArray itemList = jsonObject.getJSONArray("item");
					if (itemList != null) itemList.add(item);

					jsonObject.put("item", itemList);

					return;
				}
			}

			JSONObject jsonObject = new JSONObject();
			jsonObject.put("item", new ArrayList<>(Arrays.asList(item)));
			jsonObject.put("name", group(pm1, pm0));

			root.add(jsonObject);
		} else {
			root.add(item);
		}
	}

	private static String group(PostmanDoc... pms) {
		for (PostmanDoc pm : pms) {
			if (pm != null && StringUtil.isNotEmpty(pm.group())) {//
				return pm.group();
			}
		}

		return null;
	}

	private static String name(PostmanDoc... pms) {
		StringBuilder builder = new StringBuilder();

		for (PostmanDoc pm : pms) {
			if (pm != null && StringUtil.isNotEmpty(pm.name())) {
				builder.append("-").append(pm.name());
			}
		}

		if (builder.length() > 0 && builder.charAt(0) == '-') {
			return builder.deleteCharAt(0).toString();
		} else {
			return builder.toString();
		}

	}
}
